package com.yz.sharkim.handler;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.yz.sharkim.exception.HttpNotFoundException;
import com.yz.sharkim.exception.HttpParameterParseException;
import com.yz.sharkim.util.ConvertUtil;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;


@Slf4j
@Component
public class RouterHttp {

    private static final String URI_SEPARATOR = "&";

    private static final String PARAMETER_SEPARATOR = "=";

    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    private static final String FORM_DATA = "multipart/form-data";

    static Object processRequest(FullHttpRequest request) {
        // method object
        val value = DispatchRouterHandler.get(request);
        if (value == null) {
            throw new HttpNotFoundException(request.uri());
        }

        val element = getParameter(request);
        val parameters = value.getMethod().getParameters();
        Object[] objects = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            if (element.size() == 0) {
                objects[i] = null;
                continue;
            }
            val item = parameters[i];
            if (item.getType() instanceof Object && !item.getType().equals(String.class)) {
                objects[i] = ConvertUtil.fromJson(element, item);
            } else {
                objects[i] = element.get(item.getName()).getAsString();
            }
        }
        Object result = null;
        try {
            result = value.getMethod().invoke(value.getObject(), objects);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

    private static JsonObject getParameter(FullHttpRequest request) {
        val uri = request.uri().split(DispatchRouterHandler.URI_PARAMETER_SEPARATOR);
        // 处理URI参数
        val element = new JsonObject();
        if (uri.length == 2) {
            val array = uri[1].split(URI_SEPARATOR);
            for (String s : array) {
                val param = s.split(PARAMETER_SEPARATOR);
                if (param.length == 2) {
                    element.addProperty(param[0], param[1]);
                    log.debug("key: {}, value:{}", param[0], param[1]);
                } else {
                    element.addProperty(param[0], (String) null);
                }
            }
        }

        // 处理post 参数
        postParameter(element, request);
        return element;
    }

    private static void postParameter(JsonObject object, FullHttpRequest request) {
        if (!request.method().equals(HttpMethod.POST)) {
            return;
        }
        val contentType = request.headers().get(HEADER_CONTENT_TYPE);
        try {
            if (contentType.startsWith(FORM_DATA)) {
                formData(object, new HttpPostRequestDecoder(request).getBodyHttpDatas());
            }
            val content = request.content().toString(ConvertUtil.DEFAULT_CHARSET);
            if (!StringUtils.isEmpty(content)) {
                jsonData(object, content);
            }
        } catch (IOException e) {
            throw new HttpParameterParseException("Http parse exception " + request.toString());
        }
    }

    private static void jsonData(JsonObject object, String string) {
        val json = (JsonObject) JsonParser.parseString(string);
        for (Map.Entry<String, JsonElement> stringJsonElementEntry : json.entrySet()) {
            object.add(stringJsonElementEntry.getKey(), stringJsonElementEntry.getValue());
        }
    }

    private static void formData(JsonObject object, List<InterfaceHttpData> httpDataList) throws IOException {
        for (InterfaceHttpData bodyHttpData : httpDataList) {
            val data = (Attribute) bodyHttpData;
            object.addProperty(data.getName(), data.getValue());
        }
    }

}
